﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.PeInspector.Reflection.Disassembly;
using System.Runtime.InteropServices;

namespace Kokomo.PeInspector.Reflection
{
	class MsilDocumentBuilder : BracedCodeDocumentBuilder
	{
		static MsilDocumentBuilder()
		{
		}

		public MsilDocumentBuilder(
			ICodeWriter writer,
			MemberInfo memberContext,
			Func<MethodBase, MethodDecompilation> disassemblerDelegate
			)
			: base(writer, memberContext, disassemblerDelegate)
		{
		}

		public void WriteAttributes(IList<CustomAttributeData> customAttributes)
		{
			if (customAttributes == null) return;
		}
		public void WriteMemberDeclaration(MemberInfo member)
		{
			if (member == null) throw new ArgumentNullException("member");

			MethodBase method = member as MethodBase;
			if (method != null)
			{
				this.WriteMethodDeclaration(method);
			}
			else
			{
				FieldInfo field = member as FieldInfo;
				if (field != null)
				{
					this.WriteFieldDeclaration(field);
				}
				else
				{
					PropertyInfo property = member as PropertyInfo;
					if (property != null)
					{
						this.WritePropertyDeclaration(property);
					}
					else
					{
						EventInfo eventInfo = member as EventInfo;
						if (eventInfo != null)
						{
							this.WriteEventDeclaration(eventInfo);
						}
						else
						{
							Type type = member as Type;
							if (type != null)
							{
								this.WriteTypeDeclaration(type);
							}
						}
					}
				}
			}
		}

		private void WriteMemberReference(MemberInfo member)
		{
			if (member.DeclaringType != null)
			{
				this.WriteTypeReference(member.DeclaringType);
				this.WriteText("::");
			}
			this.Writer.WriteMemberReference(member, member.Name);
		}

		private void WriteAssemblyReference(Assembly assembly)
		{
			this.WriteText("[");
			this.Writer.WriteMemberReference(assembly, assembly.GetName().Name);
			this.WriteText("]");
		}

		private void WriteGenericParamList(Type[] genericParams)
		{
			this.Writer.WriteText("<", CodeTextType.Operator);
			for (int i = 0; i < genericParams.Length; i++)
			{
				if (i > 0) this.Writer.WriteText(", ", CodeTextType.Operator);

				Type genericParam = genericParams[i];
				this.WriteGenericParam(genericParam);
			}
			this.Writer.WriteText(">", CodeTextType.Operator);
		}
		private void WriteGenericParam(Type genericParam)
		{
			if (genericParam == null) throw new ArgumentNullException("genericParam");

			ICodeWriter writer = this.Writer;

			// Attributes
			if (genericParam.GenericParameterAttributes != GenericParameterAttributes.None)
			{
				if (genericParam.GenericParameterAttributes.HasFlag(GenericParameterAttributes.Covariant))
					writer.WriteText("+", CodeTextType.Operator);
				if (genericParam.GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant))
					writer.WriteText("-", CodeTextType.Operator);

				List<string> attributeKeywords = new List<string>();
				if (genericParam.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint))
					attributeKeywords.Add("class");
				else if (genericParam.GenericParameterAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint))
					attributeKeywords.Add("valuetype");
				if (genericParam.GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint))
					attributeKeywords.Add(".ctor");

				this.WriteKeywordsWithTrailingSpace(attributeKeywords.ToArray());
			}
			// Constraints
			Type[] genericConstraints = genericParam.GetGenericParameterConstraints();
			if (genericConstraints.Length > 0)
			{
				writer.WriteText("(", CodeTextType.Operator);
				for (int j = 0; j < genericConstraints.Length; j++)
				{
					if (j > 0) writer.WriteText(", ", CodeTextType.Operator);
					Type genericConstraint = genericConstraints[j];
					this.WriteTypeReference(genericConstraint);
				}
				writer.WriteText(")", CodeTextType.Operator);
			}

			writer.WriteText(genericParam.Name, CodeTextType.Declarator);
		}

		#region Type declaration
		private void WriteTypeReference(Type type)
		{
			if (type == null) throw new ArgumentNullException("type");

			ICodeWriter writer = this.Writer;
			string keyword = null;
			string text = null;

			// Partition II § 7.1
			if (type == typeof(bool)) keyword = "bool";
			else if (type == typeof(char)) keyword = "char";
			else if (type == typeof(float)) keyword = "float32";
			else if (type == typeof(double)) keyword = "float64";
			else if (type == typeof(sbyte)) keyword = "int8";
			else if (type == typeof(short)) keyword = "int16";
			else if (type == typeof(int)) keyword = "int32";
			else if (type == typeof(long)) keyword = "int64";
			else if (type == typeof(IntPtr)) keyword = "native int";
			else if (type == typeof(UIntPtr)) keyword = "native unsigned int";
			else if (type == typeof(object)) keyword = "object";
			else if (type == typeof(TypedReference)) keyword = "typedref";
			else if (type == typeof(byte)) keyword = "unsigned int8";
			else if (type == typeof(ushort)) keyword = "unsigned int16";
			else if (type == typeof(uint)) keyword = "unsigned int32";
			else if (type == typeof(ulong)) keyword = "unsigned int64";
			else if (type == typeof(void)) keyword = "void";

			if (keyword != null)
			{
				writer.WriteText(keyword, CodeTextType.Keyword);
			}
			else
			{
				if (type.IsGenericParameter)
				{
					if (type.DeclaringType != null)
						writer.WriteText("!" + type.GenericParameterPosition);
					else if (type.DeclaringMethod != null)
						writer.WriteText("!!" + type.GenericParameterPosition);
				}
				else if (type.IsValueType)
				{
					writer.WriteText("valuetype", CodeTextType.Keyword);
					writer.WriteText(" ");
					writer.WriteMemberReference(type, this.GetTypeReferenceText(type));
				}
				// TODO: Method pointer
				else if (type.IsByRef)
				{
					Type elementType = type.GetElementType();
					writer.WriteMemberReference(elementType, this.GetTypeReferenceText(elementType));
					writer.WriteText("&", CodeTextType.Operator);
				}
				else if (type.IsPointer)
				{
					Type elementType = type.GetElementType();
					writer.WriteMemberReference(elementType, this.GetTypeReferenceText(elementType));
					writer.WriteText("*", CodeTextType.Operator);
				}
				else if (type.IsGenericType && !type.IsGenericTypeDefinition)
				{
					writer.WriteMemberReference(type, this.GetTypeReferenceText(type));
					Type[] genericArgs = type.GetGenericArguments();
					writer.WriteText("<", CodeTextType.Operator);
					for (int i = 0; i < genericArgs.Length; i++)
					{
						if (i > 0)
							writer.WriteText(", ", CodeTextType.Operator);
						Type genericArg = genericArgs[i];
						this.WriteTypeReference(genericArg);
					}
					writer.WriteText(">", CodeTextType.Operator);
				}
				else if (type.IsArray)
				{
					Type elementType = type.GetElementType();
					int rank = type.GetArrayRank();
					writer.WriteMemberReference(elementType, this.GetTypeReferenceText(elementType));
					if (rank == 0)
						writer.WriteText("[]", CodeTextType.Operator);
					else
						writer.WriteText("[" + new string(',', rank - 1) + "]", CodeTextType.Operator);
				}
				else
				{
					if (type.IsClass)
					{
						writer.WriteText("class", CodeTextType.Keyword);
						writer.WriteText(" ");
					}
					writer.WriteMemberReference(type, this.GetTypeReferenceText(type));
				}
			}
		}
		private string GetTypeReferenceText(Type type)
		{
			if (type == null) throw new ArgumentNullException("type");

			if (type.DeclaringType != null)
				return this.GetTypeReferenceText(type.DeclaringType) + "+" + type.Name;

			return type.FullName ?? type.Name;
		}
		public void WriteTypeDeclaration(Type type)
		{
			if (type == null) throw new ArgumentNullException("type");

			ICodeWriter writer = this.Writer;
			writer.WriteText(".class", CodeTextType.Keyword);
			writer.WriteText(" ");

			// ClassHeader
			{
				// ClassAttr
				{
					List<string> attributeKeywords = new List<string>();
					// Visibility
					if (type.IsNestedAssembly) attributeKeywords.Add("nested assembly");
					else if (type.IsNestedFamANDAssem) attributeKeywords.Add("nested famandassem");
					else if (type.IsNestedFamily) attributeKeywords.Add("nested family");
					else if (type.IsNestedFamORAssem) attributeKeywords.Add("nested famorassem");
					else if (type.IsNestedPrivate) attributeKeywords.Add("nested private");
					else if (type.IsNestedPublic) attributeKeywords.Add("nested public");
					else if (!type.IsVisible) attributeKeywords.Add("private");
					else if (type.IsVisible) attributeKeywords.Add("public");
					// Layout
					if (type.IsAutoLayout) attributeKeywords.Add("auto");
					else if (type.IsExplicitLayout) attributeKeywords.Add("explicit");
					else if (type.IsLayoutSequential) attributeKeywords.Add("sequential");
					// Semantics
					if (type.IsInterface) attributeKeywords.Add("interface");
					// Inheritance
					if (type.IsAbstract) attributeKeywords.Add("abstract");
					else if (type.IsSealed) attributeKeywords.Add("sealed");
					// Interoperation
					if (type.IsAnsiClass) attributeKeywords.Add("ansi");
					else if (type.IsAutoClass) attributeKeywords.Add("autochar");
					else if (type.IsUnicodeClass) attributeKeywords.Add("unicode");
					// Special handling
					if (type.Attributes.HasFlag(TypeAttributes.RTSpecialName)) attributeKeywords.Add("rtspecialname");
					if (type.IsSerializable) attributeKeywords.Add("serializable");
					if (type.IsSpecialName) attributeKeywords.Add("specialname");
					if (type.Attributes.HasFlag(TypeAttributes.BeforeFieldInit)) attributeKeywords.Add("beforefieldinit");

					this.WriteKeywordsWithTrailingSpace(attributeKeywords.ToArray());
				}
				// ID
				writer.WriteText(type.FullName ?? type.Name, CodeTextType.Declarator);
				// GenPars
				if (type.IsGenericTypeDefinition)
				{
					Type[] genericParams = type.GetGenericArguments();
					this.WriteGenericParamList(genericParams);
				}
				writer.Indent();
				// extends
				if (type.BaseType != null)
				{
					writer.WriteLineBreak();
					writer.WriteText("extends", CodeTextType.Keyword);
					writer.WriteText(" ");
					this.WriteTypeReference(type.BaseType);
				}
				// implements
				Type[] implementedInterfaces = type.GetInterfaces();
				if (implementedInterfaces.Length > 0)
				{
					writer.WriteLineBreak();
					writer.WriteText("implements", CodeTextType.Keyword);
					writer.WriteText(" ");
					for (int i = 0; i < implementedInterfaces.Length; i++)
					{
						if (i > 0) writer.WriteText(", ", CodeTextType.Operator);
						Type interfaceType = implementedInterfaces[i];
						this.WriteTypeReference(interfaceType);
					}
				}

				writer.Outdent();
				this.OpenBlock();

				if (type.IsExplicitLayout)
				{
					StructLayoutAttribute structLayout = type.StructLayoutAttribute;
					if (structLayout != null)
					{
						this.WriteKeywordText(".pack ");
						writer.WriteText(structLayout.Pack.ToString());
						writer.WriteLineBreak();
						this.WriteKeywordText(".size ");
						writer.WriteText(structLayout.Size.ToString());
						writer.WriteLineBreak();
					}
				}
				// Attributes
				IList<CustomAttributeData> customAttributes = type.GetCustomAttributesData();
				this.WriteAttributes(customAttributes);
				// Members
				MemberInfo[] members = type.GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
				for (int i = 0; i < members.Length; i++)
				{
					MemberInfo member = members[i];
					this.WriteMemberDeclaration(member);
					writer.WriteLineBreak();
				}

				this.CloseBlock();
			}
		}
		#endregion
		#region Methods
		private void WriteMethodBody(MethodBase method)
		{
			IList<CustomAttributeData> customAttributes = method.GetCustomAttributesData();
			this.WriteAttributes(customAttributes);

			if (method.Module.Assembly.EntryPoint == method)
			{
				this.WriteKeywordText(".entrypoint");
				this.Writer.WriteLineBreak();
			}

			if (!MsilDecompiler.HasMethodBody(method))
			{
				return;
			}

			MethodBody methodBody = method.GetMethodBody();
			ParameterInfo[] parameters = method.GetParameters();
			for (int i = 0; i < parameters.Length; i++)
			{
				ParameterInfo parameter = parameters[i];
				//this.WriteKeywordText(".param ");
			}
			IList<LocalVariableInfo> locals = methodBody.LocalVariables;
			if (locals.Count > 0)
			{
				this.WriteKeywordWithTrailingSpace(".locals");
				if (methodBody.InitLocals) this.WriteKeywordWithTrailingSpace("init");
				// TODO: Locals
				this.WriteText("(");
				this.Writer.Indent();
				this.Writer.WriteLineBreak();

				int lastLocalIndex = locals.Count - 1;
				for (int i = 0; i <= lastLocalIndex; i++)
				{
					LocalVariableInfo local = locals[i];
					string localID = string.Format("[{0}]", i);
					this.Writer.WriteText(localID, CodeTextType.Declarator);
					this.WriteText(" ");
					this.WriteTypeReference(local.LocalType);

					if (i != lastLocalIndex) this.WriteText(",");
					this.Writer.WriteLineBreak();
				}
				this.WriteText(")");
				this.Writer.Outdent();
				this.Writer.WriteLineBreak();
			}
			this.WriteKeywordWithTrailingSpace(".maxstack");
			this.WriteText(methodBody.MaxStackSize.ToString());
			this.Writer.WriteLineBreak();
			{
				MethodInfo methodInfo = method as MethodInfo;
				if (methodInfo != null)
				{
					MethodInfo baseMethod = methodInfo.GetBaseDefinition();
					if (
						(baseMethod != null)
						&& (baseMethod != methodInfo)
						)
					{
						this.WriteKeywordWithTrailingSpace(".override");
						this.WriteTypeReference(baseMethod.DeclaringType);
						this.WriteText(".");
						this.WriteText(baseMethod.Name);
						this.Writer.WriteLineBreak();
					}
				}
			}

			this.Writer.WriteLineBreak();

			ICodeWriter codeWriter = this.Writer;
			IEnumerable<Instruction> instructions = MsilDecompiler.GetMsilInstructions(method);

			foreach (var instruction in instructions)
			{
				codeWriter.WriteText(string.Format("L_{0:X4}: ", instruction.Offset));
				codeWriter.WriteText(instruction.OpCode.Name, CodeTextType.Keyword);

				if (instruction.Operand != null)
				{
					codeWriter.WriteText(" ");

					Type type = instruction.Operand as Type;
					if (type != null)
					{
						this.WriteTypeReference(type);
					}
					else
					{
						MemberInfo member = instruction.Operand as MemberInfo;
						if (member != null)
						{
							this.WriteMemberReference(member);
						}
						else
						{
							MsilBranchTarget[] targets = instruction.Operand as MsilBranchTarget[];
							if (targets != null)
							{
								codeWriter.WriteText("(L_");
								codeWriter.WriteText(string.Join(", L_", targets));
								codeWriter.WriteText(")");
							}
							else
							{
								MsilBranchTarget? branchTarget = instruction.Operand as MsilBranchTarget?;
								if (branchTarget != null)
								{
									codeWriter.WriteText("L_" + branchTarget.Value.ToString());
								}
								else
								{
									System.Reflection.Emit.OperandType operandType = instruction.OpCode.OperandType;
									if (operandType == System.Reflection.Emit.OperandType.InlineVar)
									{
										string varRef = string.Format("[{0}]", instruction.Operand);
										this.WriteText(varRef);
									}
									else
									{
										string str = instruction.Operand as string;
										if (str != null)
										{
											codeWriter.WriteText(string.Format("\"{0}\"", str), CodeTextType.Literal);
										}
										else
										{
											string operandText = instruction.Operand.ToString();
											codeWriter.WriteText(operandText, CodeTextType.Literal);
										}
									}
								}
							}
						}
					}
				}

				codeWriter.WriteLineBreak();
			}

			//TableRowGroup rowGroup = new TableRowGroup();
			//foreach (var record in this.MsilRecords)
			//{
			//    string operandText = (record.Operand == null) ? string.Empty : record.Operand.ToString();
			//    rowGroup.AddRow(
			//        record.Offset.ToString("X4"),
			//        record.OpCode.Name,
			//        operandText
			//        );
			//}

			//FlowDocument flowDocument = new FlowDocument();
			//Table msilTable = new Table();
			//msilTable.RowGroups.Add(rowGroup);
			//flowDocument.Blocks.Add(msilTable);
		}
		private void WriteMethodHeader(MethodBase method)
		{
			bool isImport = method.Attributes.HasFlag(MethodAttributes.PinvokeImpl);
			DllImportAttribute dllImport = null;
			if (isImport)
				dllImport = method.GetCustomAttribute<DllImportAttribute>();
			MethodAttributes attributes = method.Attributes;
			ICodeWriter writer = this.Writer;

			// MethAttr
			{
				List<string> keywords = new List<string>();

				MethodAttributes access = attributes & MethodAttributes.MemberAccessMask;

				if (method.IsAssembly) keywords.Add("assembly");
				else if (access == MethodAttributes.MemberAccessMask) keywords.Add("compilercontrolled");
				else if (method.IsFamilyAndAssembly) keywords.Add("famandassem");
				else if (method.IsFamilyOrAssembly) keywords.Add("famorassem");
				else if (method.IsPrivate) keywords.Add("private");
				else if (method.IsPublic) keywords.Add("public");

				if (method.IsFinal) keywords.Add("final");
				else if (method.IsHideBySig) keywords.Add("hidebysig");
				else if (method.IsStatic) keywords.Add("static");
				else if (method.IsVirtual) keywords.Add("virtual");

				if (attributes.HasFlag(MethodAttributes.NewSlot)) keywords.Add("newslot");

				if (method.IsAbstract) keywords.Add("abstract");
				if (attributes.HasFlag(MethodAttributes.RTSpecialName)) keywords.Add("rtspecialname");
				if (method.IsSpecialName) keywords.Add("specialname");
				// TODO: strict ?

				this.WriteKeywordsWithTrailingSpace(keywords);

				if (dllImport != null)
				{
					this.WriteKeywordText("pinvokeimpl");
					this.WriteText("(");
					// TODO: pinvoke
					this.WriteText(")");
				}
			}
			// CallConv
			{
				WriteCallingConvention(method, dllImport);
			}

			MethodInfo methodInfo = method as MethodInfo;

			// Type
			Type returnType = (methodInfo != null) ? methodInfo.ReturnType : typeof(void);
			this.WriteTypeReference(returnType);
			this.WriteText(" ");

			// TODO: Marshal

			// MethodName
			writer.WriteText(method.Name, CodeTextType.Declarator);

			// GenPars
			if (
				(methodInfo != null)
				&& (methodInfo.IsGenericMethod)
				)
			{
				Type[] genericParameters = methodInfo.GetGenericArguments();
				this.WriteGenericParamList(genericParameters);
			}

			// Parameters
			{
				this.WriteMethodParameters(method.GetParameters());
			}

			// ImplAttr
			{
				List<string> keywords = new List<string>();
				MethodImplAttributes implAttributes = method.GetMethodImplementationFlags();

				MethodImplAttributes codeType = implAttributes & MethodImplAttributes.CodeTypeMask;
				if (codeType == MethodImplAttributes.IL) keywords.Add("cil");
				else if (codeType == MethodImplAttributes.Native) keywords.Add("native");
				else if (codeType == MethodImplAttributes.OPTIL) keywords.Add("cil");
				else if (codeType == MethodImplAttributes.Runtime) keywords.Add("runtime");

				MethodImplAttributes managed = implAttributes & MethodImplAttributes.ManagedMask;
				if (managed == MethodImplAttributes.Managed) keywords.Add("managed");
				else if (managed == MethodImplAttributes.Unmanaged) keywords.Add("unmanaged");

				if (implAttributes.HasFlag(MethodImplAttributes.ForwardRef)) keywords.Add("forwardref");
				if (implAttributes.HasFlag(MethodImplAttributes.InternalCall)) keywords.Add("internalcall");
				if (implAttributes.HasFlag(MethodImplAttributes.NoInlining)) keywords.Add("noinlining");
				if (implAttributes.HasFlag(MethodImplAttributes.Synchronized)) keywords.Add("synchronized");

				this.WriteKeywordsWithTrailingSpace(keywords);
			}
		}

		private void WriteCallingConvention(MethodBase method, DllImportAttribute dllImport)
		{
			List<string> keywords = new List<string>();

			CallingConventions convention = method.CallingConvention;
			if (!method.IsStatic)
			{
				keywords.Add("instance");
				if (convention.HasFlag(CallingConventions.ExplicitThis))
				{
					keywords.Add("explicit");
				}
			}
			//CallKind
			if (dllImport != null)
			{
				keywords.Add("unmanaged");
				switch (dllImport.CallingConvention)
				{
					case CallingConvention.Cdecl:
						keywords.Add("cdecl");
						break;
					case CallingConvention.FastCall:
						keywords.Add("fastcall");
						break;
					case CallingConvention.Winapi:
					case CallingConvention.StdCall:
						keywords.Add("stdcall");
						break;
					case CallingConvention.ThisCall:
						keywords.Add("thiscall");
						break;
					default:
						break;
				}
			}
			else
			{
				keywords.Add("default");
			}

			this.WriteKeywordsWithTrailingSpace(keywords);
		}

		private void WriteMethodParameters(ParameterInfo[] parameters)
		{
			if (parameters == null) throw new ArgumentNullException("parameters");

			if (parameters.Length == 0)
			{
				this.WriteText(" () ");
				return;
			}

			this.WriteText(" (");

			this.Writer.Indent();
			this.Writer.WriteLineBreak();

			int lastParameterIndex = parameters.Length - 1;
			for (int i = 0; i <= lastParameterIndex; i++)
			{
				ParameterInfo parameter = parameters[i];

				if (parameter.IsIn) this.WriteKeywordWithTrailingSpace("in");
				if (parameter.IsOptional) this.WriteKeywordWithTrailingSpace("opt");
				if (parameter.IsOut) this.WriteKeywordWithTrailingSpace("out");

				this.WriteTypeReference(parameter.ParameterType);
				this.WriteText(" ");

				// TODO: Marshal

				this.Writer.WriteText(parameter.Name, CodeTextType.Declarator);
				if (i != lastParameterIndex) this.WriteText(",");
				this.Writer.WriteLineBreak();
			}
			this.Writer.EnsureIndent();
			this.Writer.Outdent();
			this.WriteText(") ");
		}
		public void WriteMethodDeclaration(MethodBase method)
		{
			this.WriteKeywordText(".method");
			this.WriteText(" ");
			this.WriteMethodHeader(method);

			if (MsilDecompiler.HasMethodBody(method))
			{
				this.OpenBlock();
				this.WriteMethodBody(method);
				this.CloseBlock();
			}

			//// MethodHeader
			//// MethAttr
			//// CallConv
			//// Type
			//this.
		}
		#endregion
		#region Fields
		public void WriteFieldDeclaration(FieldInfo field)
		{
			if (field == null) throw new ArgumentNullException("field");

			ICodeWriter writer = this.Writer;
			this.WriteKeywordText(".field ");
			bool isExplicit =
				(field.DeclaringType != null)
				&& field.DeclaringType.IsExplicitLayout;

			IList<CustomAttributeData> customAttributes = field.GetCustomAttributesData();
			if (isExplicit)
			{
				ConstructorInfo fieldOffsetConstructor = typeof(FieldOffsetAttribute).GetConstructor(new Type[] { typeof(int) });
				CustomAttributeData fieldOffsetAttributeData = customAttributes.FirstOrDefault(a => a.Constructor == fieldOffsetConstructor);
				if (fieldOffsetAttributeData != null)
				{
					int fieldOffset = (int)fieldOffsetAttributeData.ConstructorArguments[0].Value;
					writer.WriteText("[", CodeTextType.Operator);
					writer.WriteText(fieldOffset.ToString());
					writer.WriteText("]", CodeTextType.Operator);
					writer.WriteText(" ");

					customAttributes.Remove(fieldOffsetAttributeData);
				}
			}

			// FieldAttr
			{
				List<string> attributeKeywords = new List<string>();

				if (field.IsAssembly) attributeKeywords.Add("assembly");
				else if (field.IsFamilyAndAssembly) attributeKeywords.Add("famandassem");
				else if (field.IsFamily) attributeKeywords.Add("family");
				else if (field.IsFamilyOrAssembly) attributeKeywords.Add("famorassem");
				else if (field.IsPrivate) attributeKeywords.Add("private");
				else if (field.IsPublic) attributeKeywords.Add("public");

				if (field.IsInitOnly) attributeKeywords.Add("initonly");
				else if (field.IsLiteral) attributeKeywords.Add("literal");

				// notserialized
				// compilercontrolled
				if (field.IsSpecialName) attributeKeywords.Add("specialname");
				if (field.Attributes.HasFlag(FieldAttributes.RTSpecialName)) attributeKeywords.Add("rtspecialname");

				if (field.IsStatic) attributeKeywords.Add("static");

				this.WriteKeywordsWithTrailingSpace(attributeKeywords);
			}

			writer.WriteText(field.Name, CodeTextType.Declarator);
			// FieldInit
		}
		#endregion
		#region Properties
		public void WritePropertyDeclaration(PropertyInfo property)
		{
			if (property == null) throw new ArgumentNullException("property");

			ICodeWriter writer = this.Writer;

			List<string> keywords = new List<string>();
			keywords.Add(".property");
			if (property.IsSpecialName) keywords.Add("specialname");
			if (property.Attributes.HasFlag(PropertyAttributes.RTSpecialName)) keywords.Add("rtspecialname");
			this.WriteKeywordsWithTrailingSpace(keywords);

			MethodInfo[] accessors = property.GetAccessors(true);
			MethodInfo accessor = accessors.FirstOrDefault();
			if (accessor != null)
				this.WriteCallingConvention(accessor, null);

			this.WriteTypeReference(property.PropertyType);
			writer.WriteText(" ");
			writer.WriteText(property.Name, CodeTextType.Declarator);

			ParameterInfo[] indexParameters = property.GetIndexParameters();
			this.WriteMethodParameters(indexParameters);

			this.OpenBlock();
			MethodInfo getter = property.GetGetMethod(true);
			MethodInfo setter = property.GetSetMethod(true);
			for (int i = 0; i < accessors.Length; i++)
			{
				accessor = accessors[i];

				string keyword;
				if (accessor == getter) keyword = ".get";
				else if (accessor == setter) keyword = ".set";
				else keyword = ".other";

				WriteAccessor(accessor, keyword);
			}

			this.CloseBlock();
		}

		private void WriteAccessor(MethodInfo accessor, string keyword)
		{
			ICodeWriter writer = this.Writer;

			writer.WriteText(keyword, CodeTextType.Keyword);
			writer.WriteText(" ");
			this.WriteCallingConvention(accessor, null);
			writer.WriteText(" ");
			this.WriteTypeReference(accessor.ReturnType);
			this.WriteText(" ");
			writer.WriteMemberReference(accessor, accessor.Name);

			ParameterInfo[] parameters = accessor.GetParameters();
			this.WriteMethodParameters(parameters);
			writer.WriteLineBreak();
		}
		#endregion
		#region Events
		public void WriteEventDeclaration(EventInfo eventInfo)
		{
			if (eventInfo == null) throw new ArgumentNullException("eventInfo");

			ICodeWriter writer = this.Writer;

			List<string> keywords = new List<string>();
			keywords.Add(".event");
			if (eventInfo.IsSpecialName) keywords.Add("specialname");
			if (eventInfo.Attributes.HasFlag(EventAttributes.RTSpecialName)) keywords.Add("rtspecialname");
			this.WriteKeywordsWithTrailingSpace(keywords);

			this.WriteTypeReference(eventInfo.EventHandlerType);
			writer.WriteText(" ");
			writer.WriteText(eventInfo.Name, CodeTextType.Declarator);

			this.OpenBlock();
			MethodInfo addMethod = eventInfo.GetAddMethod(true);
			if (addMethod != null)
				this.WriteAccessor(addMethod, ".addon");
			MethodInfo removeMethod = eventInfo.GetRemoveMethod(true);
			if (removeMethod != null)
				this.WriteAccessor(removeMethod, ".removeon");
			MethodInfo fireMethod = eventInfo.GetRaiseMethod(true);
			if (fireMethod != null)
				this.WriteAccessor(fireMethod, ".fire");
			MethodInfo[] otherMethods = eventInfo.GetOtherMethods(true);
			for (int i = 0; i < otherMethods.Length; i++)
			{
				MethodInfo otherMethod = otherMethods[i];
				this.WriteAccessor(otherMethod, ".other");
			}

			this.CloseBlock();
		}
		#endregion
	}
}
